package Offer2019;

import java.util.concurrent.TimeUnit;

/**
 * 多线程8锁问题
 * 1：标准访问，请问先打印邮件还是短信？邮件-->短信
 * 2：邮件方法暂停2秒钟，请问先打印邮件还是短信？邮件-->短信
 * 3：新增一个普通方法hello(),请问先打印邮件还是hello？hello->邮件
 * 4：两部手机，请问先打印邮件还是短信？短信-->邮件
 * 5：两个静态方法，同一部手机，请问先打印邮件还是短信？邮件-->短信
 * 6：两个静态方法，两部手机，请问先打印邮件还是短信？邮件-->短信
 * 7：一个静态方法，一个普通同步方法，一部手机，请问先打印邮件还是短信？短信-->邮件
 * 8：一个静态方法，一个普通同步方法，两部手机，请问先打印邮件还是短信？短信-->邮件
 * <p>
 * <p>
 * 一个对象里面如果有多个synchronized方法，某一时刻内，只要一个线程去调用其中的一个synchronized方法，其他线程都这能等待。
 * 换句话说，某一个时刻内，只能有唯一一个线程去访问这些synchronized方法。锁都是当前对象this，被锁定后，其他都线程都不能进入
 * 到当前对象到其他的synchronized方法
 * <p>
 * 加个普通方法后发现和同步锁无关
 * <p>
 * 换成两个对象后，不是同一把锁两，情况发生变化
 * <p>
 * 都换成静态同步方法后，情况又变化
 * 所有都非静态同步方法用的都是同一把锁-实例对象本身
 * <p>
 * synchronized实现同步的基础：Java中每一个对象都可以作为锁。
 * 具体表现为一下3种形式
 * 对于普通同步方法，锁的是当前实例对象。
 * 对于静态同步方法，锁的是当前类的Class对象。
 * 对于同步方法块，锁是 Synchronized 括号里配置的对象
 * <p>
 * 当一个线程试图访问同步代码块时，它首先必须得到锁，退出或抛出异常时必须释放锁。
 *  
 * 也就是说如果一个实例对象的非静态同步方法获取锁后，该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁，
 * 可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁，
 * 所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
 *  
 * 所有的静态同步方法用的也是同一把锁——类对象本身，
 * 这两把锁是两个不同的对象，所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
 * 但是一旦一个静态同步方法获取锁后，其他的静态同步方法都必须等待该方法释放锁后才能获取锁，
 * 而不管是同一个实例对象的静态同步方法之间，
 * 还是不同的实例对象的静态同步方法之间，只要它们同一个类的实例对象！
 */
public class Lock8 {

    public static void main(String[] args) throws InterruptedException {
        MyPhone phone = new MyPhone();
        MyPhone phone2 = new MyPhone();
        new Thread(() -> {
            try {
                phone.sendEmail();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "A").start();

        //保证A先启动
        Thread.sleep(200);

        new Thread(() -> {
            try {
                phone2.sendSMS();
                //phone.hello();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "B").start();
    }

}

class MyPhone {
    public static synchronized void sendEmail() throws Exception {
        TimeUnit.SECONDS.sleep(2);
        System.out.println("----sendEmail");

    }

    public synchronized void sendSMS() throws Exception {
        System.out.println("----sendSMS");
    }

    public static void hello() throws Exception {
        System.out.println("----hello");
    }
}


